#include "board.h"
#include "move.h"
#include "moves.h"
#include "moveGenerator.h"
#include <iostream>

using namespace std;

Board::Board() {
	this->NormalCheckers = 0;
	this->Queens = 0;
	this->Color[WHITE] = 0;
	this->Color[BLACK] = 0;
	this->roundsWithoutCapture = 0;
}

// ----------
// rozestavy hraci kameny na sachovnici tak jak maji stat na zacaktu hry
void Board::NewGame() {
	NormalCheckers = RowsMasks[0] | RowsMasks[1] | RowsMasks[6] | RowsMasks[7];
	Queens = 0;
	Color[BLACK] = RowsMasks[0] | RowsMasks[1];
	Color[WHITE] = RowsMasks[6] | RowsMasks[7];
}

// ----------
// zobrazi textovou reprezentaci hraci plochy na standartni vystup
void Board::Print() {
	for(int r = 0; r < 4; r++) {
		cout << ' ';
		cout << this->cAt(r*8);
		cout << ' ';
		cout << this->cAt(r*8 + 1);
		cout << ' ';
		cout << this->cAt(r*8 + 2);
		cout << ' ';
		cout << this->cAt(r*8 + 3);
		cout << endl;

		cout << this->cAt(r*8 + 4);
		cout << ' ';
		cout << this->cAt(r*8 + 5);
		cout << ' ';
		cout << this->cAt(r*8 + 6);
		cout << ' ';
		cout << this->cAt(r*8 + 7);
		cout << ' ';
		cout << endl;
	}
}

// ----------
// Provede tah na hraci plose
void Board::DoMove(Move aMove) {
	CheckerColor enemy = (aMove.Player == WHITE) ? BLACK : WHITE;

	
	// odebere z hraci plochy zajate kameny
	for(int c = 0; c < aMove.CapturedCount; c++) {
		BitboardUtils::TestBitAndReset(&(this->Color[enemy]), aMove.Captured[c]);
		BitboardUtils::TestBitAndReset(&(this->Queens), aMove.Captured[c]);
		BitboardUtils::TestBitAndReset(&(this->NormalCheckers), aMove.Captured[c]);
	}

	if(aMove.CapturedCount == 0) {
		this->roundsWithoutCapture++;
	}
	else {
		this->roundsWithoutCapture = 0;
	}

	// presune figuru na cilove pole
	if(BitboardUtils::TestBitAndReset(&(this->NormalCheckers), aMove.From)) {
		// ----------
		// tahy normalnim kamenem

		// zkontrolujeme zda se kamen nedostal na posledni radu => povyseni na damu
		if(((aMove.Player == BLACK) && (FieldsMasks[aMove.To[aMove.ToCount -1]] & RowsMasks[7])) ||
			 ((aMove.Player == WHITE) && (FieldsMasks[aMove.To[aMove.ToCount -1]] & RowsMasks[0]))) {
		  // povyseni na damu
			BitboardUtils::TestBitAndSet(&(this->Queens), aMove.To[aMove.ToCount -1]);
		}
		else {
		// normalni tah
		BitboardUtils::TestBitAndSet(&(this->NormalCheckers), aMove.To[aMove.ToCount -1]);
		}
	}
	else {
		// ----------
		// tahy damou
		BitboardUtils::TestBitAndReset(&(this->Queens), aMove.From);
		BitboardUtils::TestBitAndSet(&(this->Queens), aMove.To[aMove.ToCount -1]);
	}
	
	// smaze kamen z pozice odkud byl proveden tah
	BitboardUtils::TestBitAndReset(&(this->Color[aMove.Player]), aMove.From);
	BitboardUtils::TestBitAndSet(&(this->Color[aMove.Player]), aMove.To[aMove.ToCount -1]);
}

// ----------
// vrati kamen na poli index jako char
// 'o' - bily normalni kamen
// 'O' - bila dama
// 'x' - cerny normalni kamen
// 'X' - cerna dama
// '.' - prazdne pole

char Board::cAt(int index) {
	int at = this->At(index);

	switch(at) {
		case WHITE | NORMAL: return 'o';
		case WHITE | QUEEN: return 'O';
		case BLACK | NORMAL: return 'x';
		case BLACK | QUEEN: return 'X';
	}

	return '.';
}

// Urci typ figury na zadanem poli
//  0 - prazdne pole
//	2 - bily normalni kamen
//  4 - bila dama
//  3 - cerny normalni kamen
//  5 - cerna dama
unsigned int Board::At(int index) {
	unsigned int result = 0;
	bitboard mask = FieldsMasks[index];

	if((this->NormalCheckers & mask) != 0) result |= NORMAL;
	else if((this->Queens & mask) != 0) result |= QUEEN;

	if((this->Color[WHITE] & mask) != 0) result |= WHITE;
	else if((this->Color[BLACK] & mask) != 0) result |= BLACK;

	return result;
}

// ----------
// Odstrani kamen ze zadane pozice
void Board::RemoveAt(int position) {
	BitboardUtils::TestBitAndReset(&this->Color[0], position);
	BitboardUtils::TestBitAndReset(&this->Color[1], position);
	BitboardUtils::TestBitAndReset(&this->NormalCheckers, position);
	BitboardUtils::TestBitAndReset(&this->Queens, position);
}

// ----------
// testuje zda soucasna situace neodpovida konci hry
// int* winner - vraci viteze (BLACK, WHITE, -1 pro remizu)
// vraci true pokud je konec hry
bool Board::IsEndGame(int* winner, CheckerColor playerToPlay) {
	Moves moves;
	MoveGenerator* mg = new MoveGenerator();
	mg->Generate(playerToPlay, *this, &moves);
	delete mg;

	return this->IsEndGame(winner, playerToPlay, &moves);
}

bool Board::IsEndGame(int *winner, CheckerColor playerToPlay, Moves* moves) {
	// bily nema zadne kameny - cerny vyhral
	if(this->Color[WHITE] == 0) {
		*winner = BLACK;
		return true;
	}

	// cerny nema zadne kameny - bily vyhral
	if(this->Color[BLACK] == 0) {
		*winner = WHITE;
		return true;
	}

	if(moves->MovesCount == 0) {
		(playerToPlay == WHITE)? *winner = BLACK : *winner = WHITE;
		return true;
	}
	// v poslednich 20 kolech nebyl proveden zadny preskok => remiza
	// 20 kol (tah hrace + tah soupere) = 40 tahu
	if(this->roundsWithoutCapture >= 40) {
		*winner = -1;
		return true;
	}

	return false;
}
// ----------
// Provede tah a pokud nektera se nektera figura provinila proti pravidlu o povinnem skakani, odsrtani ji z hraci plochy
// aMove tah, ktery se ma provest
// vraci true pokud je vse v poradu, false kdyz byla nejka figura odstranena
bool Board::DoMoveAndCheck(Move aMove) {
	// ----------
	// vygeneruje vsechny platne tahy ze soucasne pozice
	Moves moves;
  MoveGenerator* mg = new MoveGenerator();
	mg->Generate((CheckerColor)aMove.Player, *this, &moves);
	delete mg;

	// provede tah a kontrolu
	return this->DoMoveAndCheck(aMove, &moves);
}

// ----------
// Provede tah a pokud nektera se nektera figura provinila proti pravidlu o povinnem skakani, odsrtani ji z hraci plochy
// aMove				tah, ktery se ma provest
// moves				struktura obsahujici vsechny platne tahy ze soucasne pozici (aby se nemuseli znovu generovat, kdyz jsou k dispozici)
// vraci true pokud je vse v poradu, false kdyz byla nejka figura odstranena
bool Board::DoMoveAndCheck(Move aMove, Moves* moves) {
	bool result = true;
	int removeAt = -1;
	// -----------
	// zjisti maximalni mozny pocet zajatych kamenu damou a normalim kamenem
	int maxQueenCaptures = 0;
	Move maxQueenMove;
	int maxNormalCaptures = 0;
	Move maxNormalMove;

	for(int i = 0; i < moves->MovesCount; i++) {
		if((this->Queens & FieldsMasks[moves->MovesList[i].From]) && (moves->MovesList[i].CapturedCount > maxQueenCaptures)) {
			maxQueenCaptures = moves->MovesList[i].CapturedCount;
			maxQueenMove = moves->MovesList[i];
		}
		if((this->NormalCheckers & FieldsMasks[moves->MovesList[i].From]) && (moves->MovesList[i].CapturedCount > maxNormalCaptures)) {
			maxNormalCaptures = moves->MovesList[i].CapturedCount;
			maxNormalMove = moves->MovesList[i];
		}
	}

	// je mozne skakat damou
	if(maxQueenCaptures > 0) {
		// hrac provedl skok normalnim kamanem
		if(this->NormalCheckers & FieldsMasks[aMove.From]) {
			removeAt = maxQueenMove.From;
			result = false;
		}
		// hrac provedl skok damou, ale ne v plnem rozsahu
		else if(maxQueenCaptures > aMove.CapturedCount) {
			// hrac hral s damou, ktera mela skakat => odstranime damu z pozice kam hrac tahnul
			// protoze jsme tah jiz provedli
			if(aMove.From == maxQueenMove.From) {
				removeAt = aMove.To[aMove.ToCount-1];
			result = false;
			}
			// hrac hral s jinou damou - odstranime figuru z pozice odkud mela tahnou
			else {
				removeAt = maxQueenMove.From;
			result = false;
			}
		}
	// neni mozne skakat damou ale normalni figurou
	} else if (maxNormalCaptures > 0) {
		if(maxNormalCaptures > aMove.CapturedCount) {
			// hrac hral s figurou, ktera mela skakat, ale neprovedl tah v plnem rozsahu
			if(aMove.From == maxNormalMove.From) {
				removeAt = aMove.To[aMove.ToCount-1];
			result = false;
			}
			// hrac hral s jinou figurou
			else {
				removeAt = maxNormalMove.From;
			result = false;
			}
		}
	}

	// -----------
	// provede tah
	this->DoMove(aMove);	

	if(result == false) 
		this->RemoveAt(removeAt);

	return result;
}

// -----------
// Zkontroluje zda je tah validni
// aMove			tah ke kontole
// vraci true pokud je tah validni, jinak false
bool Board::IsValidMove(Move aMove) {
	// ----------
	// vygeneruje vsechny platne tahy ze soucasne pozice
	Moves moves;
  MoveGenerator* mg = new MoveGenerator();
	mg->Generate((CheckerColor)aMove.Player, *this, &moves);
	delete mg;

	return this->IsValidMove(aMove, &moves);
}

// -----------
// Zkontroluje zda je tah validni
// aMove			tah ke kontole
// moves			struktura obsahujici vsechny platne tahy ze soucasne pozici (aby se nemuseli znovu generovat, kdyz jsou k dispozici)
// vraci true pokud je tah validni, jinak false
bool Board::IsValidMove(Move aMove, Moves* moves) {
	for(int i = 0; i < moves->MovesCount; i++) {
		if(Move::Equal(&aMove, &moves->MovesList[i])) return true;
	}

	return false;
}
